package com.cloud.datastore;

import java.util.List;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortDirection;


public class ScoreboardDatastore {

	private static ScoreboardDatastore instance;
	private static DatastoreService ds;
	
	/* CONSTANTS */
	
	private static final String BUCKET_KIND = "BUCKET";
	private static final String SCORE_KIND = "SCORE";
	
	private static final String SCORE_ID_PROPERTY = "SCORE_ID";
	private static final String SCORE_VALUE_PROPERTY = "SCORE_VALUE";
	
	private ScoreboardDatastore() {
		ds = DatastoreServiceFactory.getDatastoreService();
	}
	
	public static ScoreboardDatastore getInstance() {
		if (instance == null)
			instance = new ScoreboardDatastore();
		return instance;
	}
	
	/**
	 * 
	 * @param kind - Kind of the entity to check
	 * @param keyName - String key identifier of entity to check
	 * @return true if entity exists, false otherwise.
	 */
	private static boolean checkIfExists(String kind, String keyName) {
		Key k = KeyFactory.createKey(kind, keyName);
		Query q = new Query(k).setKeysOnly();
        int count = ds.prepare(q).countEntities(FetchOptions.Builder.withDefaults());
		return (count > 0);
	}
	
	/**
	 * 
	 * @param bucketName - Name of the bucket to be created, 
	 *                     this bucket will be used to store all 
	 *                     entities of the same kind. Bucket names
	 *                     must be unique.
	 * @return Boolean to indicate whether bucket was successfully created.
	 */
	public static boolean createBucket(String bucketName) {
		if (!checkIfExists(BUCKET_KIND, bucketName)) {
			Entity e = new Entity(BUCKET_KIND, bucketName);
			ds.put(e);
			return true;
		}
		else
			return false;
	}
	
	/**
	 * 
	 * @param bucketName - Bucket to store the score in
	 * @param id - ID of score achiever 
	 * @param score - Score value
	 * @return true if successfully added score, false otherwise
	 */
	public static boolean addScoreToBucket(String bucketName, String id, double score) {
		Key k = KeyFactory.createKey(BUCKET_KIND, bucketName);
		Query q = new Query(k);
        int count = ds.prepare(q).countEntities(FetchOptions.Builder.withDefaults());
        if (count == 0)
        	return false;
        
		Entity e = new Entity(SCORE_KIND, k);
			   e.setProperty(SCORE_ID_PROPERTY, id);
			   e.setProperty(SCORE_VALUE_PROPERTY, score);
	    ds.put(e);
	    return true;
	}
	
	/**
	 * 
	 * @param bucketName - Bucket name to fetch from
	 * @param topX - Top X number of scores
	 * @return List of entity objects sorted by score value, null if no bucket exists.
	 */
	public static List<Entity> getTopScoreOfBucket(String bucketName, int topX) {
		Key k = KeyFactory.createKey(BUCKET_KIND, bucketName);
		Query q = new Query(k);
        int count = ds.prepare(q).countEntities(FetchOptions.Builder.withDefaults());
        if (count == 0)
        	return null;
        
		q = new Query(SCORE_KIND, k).
		  addSort(SCORE_VALUE_PROPERTY, SortDirection.DESCENDING);
		return ds.prepare(q).asList(FetchOptions.Builder.withLimit(topX));
	}
	
	public static String getTopScoreStringFromEntityList(List<Entity> list) {
		String out = "";
		for (Entity e : list) {
			out += "Player: "+(String)e.getProperty(SCORE_ID_PROPERTY);
			out += " Score: "+(Double)e.getProperty(SCORE_VALUE_PROPERTY);
			out += ", ";
		}
		return out;
	}
}
